﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Paladin_Retribution_
{
    public class Main : CombatRoutine
    {
        public override string Name { get { return ""; } }
        public override WoWClass Class { get { return WoWClass.None; } }
        LocalPlayer Me { get { return StyxWoW.Me; } }

        private Composite _preCombatBuffs, _combatBuffs, _Heal, _Combat;
        public override Composite PreCombatBuffBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _preCombatBuffs; } }
        public override Composite CombatBuffBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _combatBuffs; } }
        public override Composite HealBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _Heal; } }
        public override Composite CombatBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _Combat; } }

        #region Initialize
        public override void Initialize()
        {
            _preCombatBuffs = createPreCombatBuffs();
            _combatBuffs = createCombatBuffs();
            _Heal = createHeal();
            _Combat = createCombat();
        }
        #endregion

        #region Pre-Combat Buffs
        Composite createPreCombatBuffs()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        ));
        }
        #endregion

        #region Combat Buffs
        Composite createCombatBuffs()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        ));
        }
        #endregion

        #region Heal
        Composite createHeal()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        ));
        }
        #endregion

        #region Combat
        Composite createCombat()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        ));
        }
        #endregion

        #region Methods

        #region Buff Checks
        public bool buffExists(int Buff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == Buff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == Buff);
                if (Results != null)
                    return true;
            }
            return false;
        }
        //public bool buffExists(int Buff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Buff);
        //        if (Results != null)
        //            return true;
        //    }
        //    return false;
        //}
        public double buffTimeLeft(int Buff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == Buff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == Buff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }
        //public double buffTimeLeft(int Buff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Buff);
        //        if (Results != null)
        //        {
        //            if (Results.TimeLeft.TotalMilliseconds > 0)
        //                return Results.TimeLeft.TotalMilliseconds;
        //        }
        //    }
        //    return 0;
        //}
        public uint buffStackCount(int Buff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == Buff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == Buff);
                if (Results != null)
                    return Results.StackCount;
            }
            return 0;
        }
        //public uint buffStackCount(int Buff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Buff);
        //        if (Results != null)
        //            return Results.StackCount;
        //    }
        //    return 0;
        //}
        #endregion

        #region Cache Checks
        public IEnumerable<WoWAura> cachedAuras = new List<WoWAura>();
        public IEnumerable<WoWAura> cachedTargetAuras = new List<WoWAura>();
        public void getCachedAuras()
        {
            if (Me.CurrentTarget != null)
                cachedTargetAuras = Me.CurrentTarget.GetAllAuras();
            cachedAuras = Me.GetAllAuras();
        }
        #endregion

        #region Cooldown Checks
        long lastUsed;
        public TimeSpan cooldownLeft(int Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.CooldownTimeLeft;
                    return TimeSpan.MaxValue;
                }
            }
            return TimeSpan.MaxValue;
        }
        public bool onCooldown(int Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.Cooldown;
                    return false;
                }
            }
            return false;
        }
        public TimeSpan spellCooldownLeft(int Spell, bool useTracker = false)
        {
            if (useTracker)
                return cooldownLeft(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Override.CooldownTimeLeft;
            }
            return TimeSpan.MaxValue;
        }
        public bool spellOnCooldown(int Spell, bool useTracker = false)
        {
            if (useTracker)
                return !onCooldown(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
                return results.Override != null ? results.Override.Cooldown : results.Original.Cooldown;
            return false;
        }
        private Dictionary<WoWSpell, long> Cooldowns = new Dictionary<WoWSpell, long>();
        public void spellUsed(int spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                Cooldowns[Result] = Result.CooldownTimeLeft.Ticks + DateTime.Now.Ticks;
            }
        }
        #endregion

        #region Debuff Checks
        public bool debuffExists(int debuff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == debuff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == debuff);
                if (Results != null)
                    return true;
            }
            return false;
        }
        //public bool debuffExists(int Debuff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Debuff);
        //        if (Results != null)
        //            return true;
        //    }
        //    return false;
        //}
        public double debuffTimeLeft(int debuff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == debuff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == debuff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }
        //public double debuffTimeLeft(int Debuff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(Debuff);
        //        if (Results != null)
        //        {
        //            if (Results.TimeLeft.TotalMilliseconds > 0)
        //                return Results.TimeLeft.TotalMilliseconds;
        //        }
        //    }
        //    return 0;
        //}
        public uint debuffStackCount(int debuff, WoWUnit onTarget, bool onMe = false)
        {
            if (onTarget != null)
            {
                var Results = !onMe ? cachedAuras.FirstOrDefault(A => A.SpellId == debuff) : cachedTargetAuras.FirstOrDefault(A => A.SpellId == debuff);
                if (Results != null)
                    return Results.StackCount;
            }
            return 0;
        }
        //public uint debuffStackCount(int debuff, WoWUnit onTarget)
        //{
        //    if (onTarget != null)
        //    {
        //        var Results = onTarget.GetAuraById(debuff);
        //        if (Results != null)
        //            return Results.StackCount;
        //    }
        //    return 0;
        //}
        #endregion

        #region Macro Checks
        public bool useCDs { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return useCDs and 0 or 1", 0)); } }
        public bool Manual { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return Manual and 0 or 1", 0)); } }
        #endregion

        #region Target Checks
        public bool gotTarget { get { return Me.GotTarget && Me.CurrentTarget != null && (Me.CurrentTarget.IsAlive && !Me.CurrentTarget.IsFriendly || Me.CurrentTarget.Name == "Training Dummy"); } } //create Dummy return
        public double targetDistance(WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.DistanceSqr;
                if (Results != null)
                    return onTarget.DistanceSqr;
            }
            return 0;
        }
        #endregion

        #region Spell Casting
        private delegate T Selection<out T>(object Context);
        Composite Cast(int Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCanCast = false)
        {
            return
                new Decorator(ret =>
                {
                    if (reqs != null && !reqs(ret))
                        return false;
                    if (!ignoreCanCast && !SpellManager.CanCast(Spell, onTarget != null ? onTarget(ret) : Me.CurrentTarget))
                        return false;
                    return true;
                },
                    new Action(ret =>
                    {
                        WoWSpell castingSpell = Me.CastingSpell;
                        if (castingSpell != null && castingSpell.Id != Spell)
                            Lua.DoString("SpellStopCasting()");
                        Logging.Write("Casting: " + Spell + " on " + Me.CurrentTarget.Name);
                        SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
                        spellUsed(Spell);
                    })
                    );
        }
        Composite dropCast(int Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCanCast = false)
        {
            return
                new Decorator(ret =>
                {
                    if (reqs != null && !reqs(ret))
                        return false;
                    if (!ignoreCanCast && !SpellManager.CanCast(Spell, onTarget != null ? onTarget(ret) : Me.CurrentTarget))
                        return false;
                    return true;
                },
                    new Action(ret =>
                    {
                        WoWSpell castingSpell = Me.CastingSpell;
                        if (castingSpell != null && castingSpell.Id != Spell)
                            Lua.DoString("SpellStopCasting()");
                        Logging.Write("Casting: " + Spell + " on " + Me.CurrentTarget.Name);
                        SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
                        SpellManager.ClickRemoteLocation(Me.CurrentTarget.Location);
                        spellUsed(Spell);
                    })
                    );
        }
        #endregion

        #endregion

        //add channel casting and unit region... what does Truth do? ~> Truth sets the return to the tracker or the spellmanager. Use spellmanager for spells that can reset and tracker for spells that do not reset
    }
}